import { CydonElement, define, watch } from 'cydon'
import { CPagination } from './CPagination'
import { API, WSAPI } from '../api'
import { MethodsStartsWith, error } from '../utils'
import { DataFilter, Filter, getFilters } from './DataFilter'
import './CPagination'

/**
 * @element c-table
 * 分页表格
 */
@define('c-table')
export class CTable<T extends {} = {}> extends CydonElement {
	static observedAttributes: readonly string[] = ['col-keys', 'object-name', 'per-pages']

	/** 数据 */
	items: T[] = []
	/** 分页器 */
	page!: CPagination
	/** 筛选器 */
	filter!: DataFilter

	/** 键名 */
	keys!: string[]
	/** 列名 */
	columns!: string[]

	/** 对象名 */
	objectName!: keyof MethodsStartsWith<WSAPI, 'get'>

	/** 排序方向 */
	get sortDir() {
		const dir = this.filter.sortKeys.find(({ index }) => index == this.k)?.dir
		return dir ? dir == 5 ? ' 随机' : (dir & 1 ? '↑' : '↓') : ''
	}

	k!: number

	connectedCallback() {
		super.connectedCallback()
		// eslint-disable @typescript-eslint/no-this-alias
		const that = this
		that.filter.keys = that.keys
		that.filter.columns = that.columns
		// 监听筛选器变化
		let f: DataFilter
		watch(this.filter, function (this: DataFilter) {
			if (f)
				updateContents(that, f, that.page).catch(error)
			else
				f = this
		})
		watch(this.filter, function (this: DataFilter) {
			if (this.sortKeys && this.localFilters)
				that.updateValue('filter')
		})
		// 监听分页器的页码变化
		watch(this.page, function (this: CPagination) {
			// 获取数据
			updateContents(that, f, this).catch(error)
		})
	}

	attributeChangedCallback(name: string, _oldVal: string, newVal: string) {
		if (name == 'col-keys') {
			const keys = ['', ...newVal.split('\n')].map(x => x.trim().split(';'))
			this.keys = keys.map(x => x[0])
			this.columns = keys.map(x => x[1])
		}
		if (name == 'object-name')
			this.objectName = <any>newVal
		if (name == 'per-pages')
			queueMicrotask(() =>
				this.page.perPages = newVal.split(',').map(x => parseInt(x)))
	}

	/** 排序 */
	sortBy(index: number) {
		const { sortKeys } = this.filter
		const i = sortKeys.findIndex(({ index: i }) => i == index)
		if (sortKeys[i]?.dir) {
			if (sortKeys[i].dir == 2)
				sortKeys.splice(i, 1)
			else
				sortKeys[i].dir = 2
		} else
			sortKeys.push({ index, dir: 1 })
	}

	shouldHide(item: Record<string, any>) {
		return shouldHide(this.filter.localFilters, item)
	}
}

function shouldHide(localFilters: Filter[], item: Record<string, any>) {
	let show = +!localFilters.length, lastconj = 3
	for (const filter of localFilters) {
		const { key, op, value, conj, valid } = filter
		if (valid) {
			const val = item[key]
			const cond = +(op >= 4 ? (typeof val == 'number' ?
				val >= +value : value.localeCompare(val) <= 0) :
				op > 1 ? RegExp(value).test(val) :
					(val + '').includes(value)) ^ (op & 1)
			if (lastconj > 2) show = cond
			else if (lastconj == 0)
				show! &= cond
			else if (lastconj == 1)
				show! |= cond
			else
				show! ^= cond
		}
		lastconj = conj
	}

	return !show
}

const updateContents = (table: CTable, f: DataFilter, p: CPagination) => (<WSAPI['getCR']>API[`get${table.objectName}`])(
	getFilters(f),
	f.getSortPattern(table.keys),
	(p.pageNum - 1) * p.perPage, p.perPage).then(([count, ...items]) => {
		p.data.total = count
		table.data.items = items
	})

declare global {
	interface HTMLElementTagNameMap {
		'c-table': CTable
	}
}
